using System;
using System.Data;
using Neo.Core;
using Neo.Core.Qualifiers;
using Neo.Database;

namespace Neo.SqlClient
{

	///<summary>
	///</summary>
	public class SqlCommandBuilder : GenericSql92Builder
	{
		//--------------------------------------------------------------------------------------
		//	Fields and Constructor
		//--------------------------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlCommandBuilder"/> class.
        /// </summary>
        /// <param name="aTable">A table.</param>
        /// <param name="aParamFactory">A param factory.</param>
		public SqlCommandBuilder(DataTable aTable, IDbImplementationFactory aParamFactory) : base(aTable, aParamFactory)
		{
		}

		
		//--------------------------------------------------------------------------------------
		//  SELECT
		//--------------------------------------------------------------------------------------

        /// <summary>
        /// Writes the select.
        /// </summary>
        /// <param name="fetchSpec">The fetch spec.</param>
		public override void WriteSelect(IFetchSpecification fetchSpec)
		{
			emap = fetchSpec.EntityMap;

			builder.Append("SELECT ");

			if(fetchSpec.FetchLimit != -1)
			{
				builder.Append("TOP ");
				builder.Append(fetchSpec.FetchLimit);
				builder.Append(" ");
			}

			WriteColumns(table.Columns);

			builder.Append(" FROM ");
			WriteIdentifier(table.TableName);

			if(fetchSpec.Qualifier != null)
			{
				builder.Append(" WHERE ");
				WriteQualifier(fetchSpec.Qualifier);
			}

			if(fetchSpec.SortOrderings != null)
			{
				builder.Append(" ORDER BY ");
				WriteSortOrderings(fetchSpec.SortOrderings);
			}

		}

        protected override void WriteQualifier(Qualifier qualifier)
        {
            qualifier.AcceptVisitor(this);
        }

        /// <summary>
        /// Visits the column qualifier.
        /// </summary>
        /// <param name="q">The q.</param>
        /// <returns></returns>
        public override object VisitColumnQualifier(ColumnQualifier q)
        {
            bool isContainsPredicate = false;
            if (q.Predicate is ContainsPredicate || q.Predicate is StringArrayPredicateBase)
            {
                isContainsPredicate = true;
            }
            else
            {
                WriteIdentifier(q.Column);
            }

            if (q.Predicate.Value == DBNull.Value)
            {
                if (q.Predicate is NotEqualPredicate)
                    builder.Append(" IS NOT NULL ");
                else if (q.Predicate is EqualsPredicate)
                    builder.Append(" IS NULL ");
                else
                    throw new ArgumentException("Invalid predicate with null value; found " + q.Predicate.GetType().FullName);
            }
            else
            {
                // Sequence matters because predicates inherit from each other.
                if (q.Predicate is NotEqualPredicate)
                    builder.Append("<>");
                else if (q.Predicate is EqualsPredicate)
                    builder.Append("=");
                else if (q.Predicate is LessThanPredicate)
                    builder.Append("<");
                else if (q.Predicate is LessOrEqualPredicate)
                    builder.Append("<=");
                else if (q.Predicate is GreaterThanPredicate)
                    builder.Append(">");
                else if (q.Predicate is GreaterOrEqualPredicate)
                    builder.Append(">=");
                else if (q.Predicate is LikePredicate)
                    builder.Append(" LIKE ");
                else if (q.Predicate is ContainsPredicate)
                    builder.Append(" CONTAINS ");
                else if (q.Predicate is StringArrayPredicateBase)
                    builder.Append(" CONTAINS ");
                else if (q.Predicate is CaseInsensitiveEqualsPredicate)
                    builder.Append(" = ");
                else
                    throw new ArgumentException("Invalid predicate in qualifier; found " + q.Predicate.GetType().FullName);

                // add the current parameter count as suffix to ensure names are unique
                DataColumn column = table.Columns[q.Column];
                string pname = ConvertToParameterName(column.ColumnName + parameters.Count);
                IDataParameter param = implFactory.CreateParameter(column, pname, q.Predicate.Value);
                if (isContainsPredicate)
                {
                    builder.Append(q.Predicate.ToString());
                }
                else
                {
                    builder.Append(pname);
                    parameters.Add(param);
                }
            }
            return null;
        }

	    //--------------------------------------------------------------------------------------
		//  Conversions
		//--------------------------------------------------------------------------------------
	
		protected override string ConvertToParameterName(string column)
		{
			string paramName = column;
			paramName = paramName.Replace(@"\", "_");
			paramName = paramName.Replace(@"/", "_");
			paramName = paramName.Replace(@"'", "_");
			paramName = paramName.Replace(@"=", "_");
			paramName = paramName.Replace("\"", "_");
			paramName = paramName.Replace(@"-", "_");
			paramName = paramName.Replace(@" ", "_");
			return "@" + paramName;
		}

        protected override string ConvertToDelimitedIdentifier(string identifier) {
            return String.Format("[{0}]", identifier);
        }



	}
}
